#include "a9main.h"
#include "sqlite3_api.h"
#include "sqlite3_global.h"
#include "sem.h"
#include "gprs.h"


#define DEV_NODE_SMS "/dev/ttyUSB1"
#define DEV_NODE_ZIG "/dev/ttyUSB0"

//记得添加低于温度最小设定值时的关设备命令和警告发生时的蜂鸣器开命令

void *M0_rcv();
void *M0_ana();
void *CGI_rcv();
void *M0_snd();
void send_m0_cmd(M0_cmd cmd);


status old_status = 
{
	.tem_max = 28,
	.tem_min = 10,
	.light_max = 1200,
	.light_min = 200,
	.hum_max = 44,
	.hum_min = 0,
};

//线程互斥锁和条件变量初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥操作线程运行状态全局变量的锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //M0数据分析线程等待的条件，由M0数据接收线程唤醒 
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;   //互斥操作全局变量old_status的锁

pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;//控制M0数据发送线程是否运行的条件变量 
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;  //互斥ZIGBEE读写操作的锁
pthread_cond_t cond2 = PTHREAD_COND_INITIALIZER;  
pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;  //互斥ZIGBEE读写操作的锁

//ZIGBEE初始化函数
struct termios options;//termios用于提供一个常规终端接口，用于控制非同步通信
void zigbee_init(int fd)
{
	tcgetattr(fd, &options);//tcgetattr 用于获取与终端相关的参数
	options.c_cflag |= ( CLOCAL | CREAD );//修改控制模式：CLOCAL保证程序不会占用串口
	//CREAD使得能够从串口中读取输入数据
	options.c_cflag &= ~CSIZE;
	options.c_cflag &= ~CRTSCTS;
	options.c_cflag |= CS8;
	options.c_cflag &= ~CSTOPB; 
	options.c_iflag |= IGNPAR;//输入模式标志
	options.c_iflag &= ~(ICRNL | IXON);
	options.c_oflag = 0;//输出模式标志
	options.c_lflag = 0;

	options.c_cc[VMIN] =sizeof(sendEnv);//zigbee发送的数据长度
	options.c_cc[VTIME] = 10;
	tcflush(fd, TCIFLUSH);//如果发生数据溢出，接收数据，但是不再读取

	cfsetispeed(&options, B115200);//设置比特率
	cfsetospeed(&options, B115200);//设置波特率
	tcsetattr(fd,TCSANOW,&options);//激活配置,tcsanow不等数据传出完毕立即更改属性  option 为参数
}


int fd = -1;
int fd_zigbee;
int old_flags;//避免货物重复入库和出库

int auto_status = ON;//网页端是否为自动控制

//和CGI进程通信的共享内存以及配合的信号量
int shmid, semid, shmid_set;
int msgid;

sendEnv *shm_addr;
status *shm_addr_set;
linkqueue_t * M0message_queue; 
int M0_rcv_status = RUN;   // M0数据接收子线程默认状态为运行 
int M0_ana_status = STOP; // M0数据处理子线程默认状态为停止，需由M0数据接收线程唤醒
int CGI_rcv_status = RUN;  //与CGI进程通信并获取网页端命令的子线程默认状态为运行
int GPRS_status = STOP;  //GPRS短信模块发送短信的子线程默认状态为停止，需由CGI接收分析子线程唤醒
int SQL_status = STOP; // 数据库写入线程默认状态为停止，需由CGI接收分析子线程唤醒
int M0_snd_status = STOP; //A9向M0发送命令子线程默认状态为停止，需由M0数据接收线程唤醒


//以下为GPRS初始化第一步
/* 1-全局结构体 */
pdu_t pdu = {
	._1_part = "089168",
	._2_phno_center = "3108100005F0",  //北京中心"3108100005F0"
	._3_part = "11000D9168",
	._5_part = "000800",
};

int led_status = OFF;
int buzzer_status = OFF;
int fen_status = OFF;

//定义命令如下
M0_cmd fen_on, fen_off, led_on, led_off, buzzer_on, buzzer_off;

int main(int argc, const char *argv[])
{

	fen_on.dev = FENGSHAN;
	fen_on.command = ON;

	led_on.dev = LED;
	led_on.command = ON;

	buzzer_on.dev = BUZZER;
	buzzer_on.command = ON;

	fen_off.dev = FENGSHAN;
	fen_off.command = OFF;

	led_off.dev = LED;
	led_off.command = OFF;

	buzzer_off.dev = BUZZER;
	buzzer_off.command = OFF;


	//以下为GPRS初始化其余步骤
#define send_message(log) send_sms(fd, &pdu, log)
	/* 2-获得流 */
	if((fd = open(DEV_NODE_SMS, O_RDWR|O_NOCTTY)) < 0){
		perror("fail to open serial message:");
	}
	/* 3-初始化串口 */
	if(init_serial(fd) < 0){
		puts("fail to init_serial()....");
	}

	/* 4-设置号码 */
	set_phno(&pdu, PHNO_RECV, "15611599785");
	set_phno(&pdu, PHNO_CENT, "13800351500");

	//创建共享最大最小各项设置值的共享内存
	key_t key2;
	key2 = ftok("/app",'k');

	if((shmid_set = shmget(key2, sizeof(status), IPC_CREAT|IPC_EXCL|0666))  == -1)
	{
		if(errno == EEXIST)
		{
			printf("the shm is already exist\n");
			shmid_set  = shmget(key2,0,0);
		}else
		{
			perror("fail to shmget");
			exit(1);
		}
	}
	if((shm_addr_set = (status *)shmat(shmid_set,NULL,0)) == (status *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}
	memset(shm_addr_set, 0, sizeof(status));


	//初始化和CGI进程通信的共享内存以及配合的信号量
	key_t key1;
	key1 = ftok("/app",'g');

	semid  = semget(key1, 1, IPC_CREAT|IPC_EXCL|0666);
	if( semid == -1)
	{
		if(errno == EEXIST)
		{
			semid = semget(key1,1,0666);
		}else{
			perror("semget");
			exit(1);
		}
	}
	init_sem(semid, 0, 1);

	if((shmid = shmget(key1, sizeof(sendEnv), IPC_CREAT|IPC_EXCL|0666))  == -1)
	{
		if(errno == EEXIST)
		{
			printf("the shm is already exist\n");
			shmid  = shmget(key1,0,0);
		}else
		{
			perror("fail to shmget");
			exit(1);
		}
	}
	if((shm_addr = (sendEnv *)shmat(shmid,NULL,0)) == (sendEnv *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}
	memset(shm_addr, 0, sizeof(sendEnv));
	//初始化发送队列,用来存放需要通过ZIGBEE发送的M0控制信息，即M0_cmd结构体信息
	M0message_queue = linkqueue_create(); 	
	//创建数据库并打开


	//以下为消息队列的初始化,用来接收CGI进程发来的命令
	key_t key;

	if((key = ftok("/app", 'g')) < 0)
	{
		perror("ftok");
		exit(1);
	}

	msgid =msgget(key,IPC_CREAT|IPC_EXCL|0666);
	if(msgid == -1)
	{
		if(errno == EEXIST)
		{
			msgid = msgget(key,0666);
		}else{
			perror("fail to magget");
			exit(1);
		}
	}

	//ZIGBEE的初始化
	fd_zigbee = open(DEV_NODE_ZIG,O_RDWR);
	if(fd_zigbee < 0)
	{
		perror("fail to open zigbee");
	}
	zigbee_init(fd_zigbee);
	printf("初始化成功\n");


	/*****************************************************************************/
	/*--------------------到此为止主程序的初始化工作已完成-----------------------*/
	/*****************************************************************************/

	send_m0_cmd(fen_off);
	printf("%s %d\n", __func__, __LINE__);
	send_m0_cmd(buzzer_off);
	printf("%s %d\n", __func__, __LINE__);
	send_m0_cmd(led_off);
	printf("%s %d\n", __func__, __LINE__);

	/*子线程ID分配与创建
	 *child_tid[0]表示M0数据接收子线程
	 *child_tid[1]表示M0数据处理子线程
	 *child_tid[2]表示与CGI进程通信并获取网页端命令的子线程
	 *child_tid[3]表示GPRS短信模块发送短信的子线程
	 *child_tid[4]表示数据库写入线程
	 *child_tid[5]表示A9向M0发送命令的线程
	 */


	pthread_t child_tid[10];  
	int err;
	err = pthread_create(&child_tid[0],NULL,(void *)M0_rcv,NULL);  
	if (err != 0)  
	{  
		printf("can't create thread:%s\n", strerror(err));  
	}  
	err = pthread_create(&child_tid[1],NULL,(void *)M0_ana,NULL);  
	if (err != 0)  
	{  
		printf("can't create thread:%s\n", strerror(err));  
	}  

	err = pthread_create(&child_tid[2],NULL,(void *)CGI_rcv,NULL);  
	if (err != 0)  
	{  
		printf("can't create thread:%s\n", strerror(err));  
	}  
	pthread_join(child_tid[0], NULL);
	pthread_join(child_tid[1], NULL);
	pthread_join(child_tid[2], NULL);
	pthread_join(child_tid[5], NULL);

	close(fd);
	return 0;
}

//--------------与child_tid[0]绑定的M0数据接收子线程----------
void *M0_rcv()
{
	sendEnv cmdrcv;
	while(1)
	{
		pthread_mutex_lock(&mutex2); //ZIGBEE读写互斥上锁 
		options.c_cc[VMIN] = sizeof(sendEnv);//zigbee接收的数据长度
		tcsetattr(fd,TCSANOW,&options);//tcsanow不等数据传出完毕立即更改属性  option 为参数
		ssize_t byte = read(fd_zigbee, &cmdrcv, sizeof(sendEnv));
		if(byte < 0)
		{
			tcflush(fd_zigbee, TCIFLUSH);
			sleep(1);
			continue;
		}
		pthread_mutex_unlock(&mutex2); //ZIGBEE读写互斥解锁 

		sem_p(semid, 0);  //用来与CGI进程读写共享内存互斥
		memcpy(shm_addr, &cmdrcv, sizeof(sendEnv));//此处需要将接收到的最新环境结构体拷贝到共享内存中
		sem_v(semid, 0);

		pthread_mutex_lock(&mutex);  
		M0_ana_status = RUN;//唤醒M0数据分析线程
		pthread_cond_signal(&cond);  
		pthread_mutex_unlock(&mutex); 

		printf("temp:%d.%d,  hum:%d.%d, x:%d, y:%d, z:%d light:%d,goods_flags:%d\n", \
				cmdrcv.temp[0], cmdrcv.temp[1], cmdrcv.hum[0], cmdrcv.hum[1],\
				cmdrcv.x, cmdrcv.y, cmdrcv.z, cmdrcv.light, cmdrcv.goods_flags);
		usleep(300);
	}
}

void send_m0_cmd(M0_cmd cmd)
{
	printf("%s %d\n", __func__, __LINE__);
	printf("dev:%d command :%d\n", cmd.dev, cmd.command);

	int command = (cmd.command == ON) ? ON : OFF;
	switch (cmd.dev)
	{
	case LED:
		led_status = command;
		break;
	case BUZZER:
		buzzer_status = command;
		break;
	case FENGSHAN:
		fen_status = command;
		break;
	}

	pthread_mutex_lock(&mutex2);  
	options.c_cc[VMIN] = sizeof(M0_cmd);//zigbee send的数据长度
	tcsetattr(fd,TCSANOW,&options);//tcsanow不等数据传出完毕立即更改属性  option 为参数
	if(write(fd_zigbee, &cmd, sizeof(M0_cmd)) < 0)
	{
		perror("fail to write zigbee");
	}
	usleep(50);
	printf("%s %d\n", __func__, __LINE__);
	pthread_mutex_unlock(&mutex2);  
}

//child_tid[1]表示M0数据处理子线程
void *M0_ana()
{	
	while(1)
	{
		pthread_mutex_lock(&mutex);  
		while(RUN != M0_ana_status)  
		{  
			pthread_cond_wait(&cond, &mutex);  
		} 
		M0_ana_status = STOP;
		pthread_mutex_unlock(&mutex);  

		if (auto_status == OFF)
		{
			continue;
		}

		sendEnv anamsg;
		sem_p(semid,0);
		memcpy(&anamsg, shm_addr, sizeof(sendEnv));
		sem_v(semid,0);

		pthread_mutex_lock(&mutex1);
		if (((old_status.tem_min) < anamsg.temp[0]) && (anamsg.temp[0] < (old_status.tem_max)))
		{
			if (fen_status == ON)
			{
	printf("%s %d\n", __func__, __LINE__);
	printf("dev:%d command :%d\n", fen_off.dev, fen_off.command);
				send_m0_cmd(fen_off);			
			}
		}
		else
		{
			if (anamsg.temp[0] > old_status.tem_max)
			{
				if (fen_status == OFF)
				{
	printf("%s %d\n", __func__, __LINE__);
	printf("dev:%d command :%d\n", fen_on.dev, fen_on.command);
					send_m0_cmd(fen_on);
					printf("%s %d\n", __func__, __LINE__);
					if(send_message("现在温度过高，已开启风扇") <0){
						puts("发送短信可能失败");
					}else{
						puts("发送短信成功");
					}
				}
			}
			else if(anamsg.temp[0] < old_status.tem_min) 
			{
				if (fen_status == ON)
				{
					if(send_message("仓库温度过低，请注意") <0){
						puts("发送短信可能失败");
					}else{
						puts("发送短信成功");
					}	
					send_m0_cmd(fen_off);			
				}
			}
		}

		if (((old_status.hum_min+10) < anamsg.hum[0]) && (anamsg.hum[0] < (old_status.hum_max-10)))
		{
			if (buzzer_status == ON)
			{
				send_m0_cmd(buzzer_off);
			}
		}
		else
		{
			if (anamsg.hum[0] > old_status.hum_max)
			{
				if (buzzer_status == OFF)
				{
					send_m0_cmd(buzzer_on);
					if(send_message("仓库湿度过高，已开启风扇") <0){
						puts("发送短信可能失败");
					}else{
						puts("发送短信成功");
					}
				}
			}
			else if(anamsg.hum[0] < old_status.hum_min) 
			{
				if (buzzer_status == OFF)
				{
					send_m0_cmd(buzzer_on);
					if(send_message("仓库环境过于干燥，请注意") <0)
					{
						puts("发送短信可能失败");
					}else{
						puts("发送短信成功");
					}
				}
			}
		}

		//三轴信息暂未进行任何判断和处理

		if (anamsg.light < old_status.light_min)
		{
			if (led_status == OFF)
			{
	printf("%s %d\n", __func__, __LINE__);
	printf("dev:%d command :%d\n", led_on.dev, led_on.command);
				send_m0_cmd(led_on);
				if(send_message("仓库光照太弱，已开启照明") <0){
					puts("发送短信可能失败");
				}else{
					puts("发送短信成功");
				}
			}
		}
		else 
		{
			if (led_status == ON)
			{
	printf("%s %d\n", __func__, __LINE__);
	printf("dev:%d command :%d\n", led_off.dev, led_off.command);
				send_m0_cmd(led_off);
			}
		}

		if (anamsg.light > old_status.light_max)
		{
			if(send_message("仓库光照过强，请注意") <0){
				puts("发送短信可能失败");
			}else{
				puts("发送短信成功");
			}
		}		
		pthread_mutex_unlock(&mutex1);  


		//标志为0无任何信息，不许要更改数据库
		//标志为1表示货物出库，需要删减数据库信息
		//标志为2表示货物入库，需要增加数据库信息
		if (anamsg.goods_flags == GOODS_NULL )
		{
			//无货物更改信息，跳出循环
			old_flags = GOODS_NULL;
			continue;
		}
		else if ((anamsg.goods_flags == GOODS_ADD) && (old_flags != GOODS_ADD))
		{
			printf("**************add******************\n");   ///
			//此处需要填写将数据插入数据库的代码
			//
			add_goods(&sqt,&anamsg);
			old_flags = GOODS_ADD;
			printf("%s %d\n", __func__, __LINE__);
			printf("**************add******************\n");   ///
		}
		else if ((anamsg.goods_flags == GOODS_SUB) && (old_flags != GOODS_SUB))
		{
			printf("**************sub******************\n");   ///
			//此处需要填写删除数据库的信息
			del_goods(&sqt, &anamsg);
			old_flags = GOODS_SUB;
			printf("%s %d\n", __func__, __LINE__);
			printf("**************sub******************\n");   ///
		}
	}
}

//child_tid[2]表示与CGI进程通信并获取网页端命令的子线程
void * CGI_rcv()
{
	M0_cmd m0;
	CGI_cmd cmdbuf;
	GPRS_cmd cmdbuf1;
	while(1)
	{
		msgrcv(msgid,(void *)&cmdbuf,sizeof(CGI_cmd)-sizeof(long),(long)1,0);
		if(cmdbuf.type == M0_ctl)
		{
			//如果是控制M0的命令，直接将其拷贝出来，添加到M0控制命令的队列中
			memcpy(&m0, &cmdbuf.text[0],sizeof(M0_cmd));
			printf("cmd:%d,  dev:%d  command:%d\n", cmdbuf.type, m0.dev, m0.command);
			if(m0.dev == auto_ctl)
			{
				auto_status = m0.command;
			}
			else
			{
				send_m0_cmd(m0);
			}
		}
		else if(cmdbuf.type == set_ctl)
		{
			//如果是修改设定值的命令，直接拷贝结构体到原有结构体中
			pthread_mutex_lock(&mutex1);  
			memcpy(&old_status, &cmdbuf.text[0], sizeof(status)); 	
			pthread_mutex_unlock(&mutex1);  
			printf("%s %d\n", __func__, __LINE__);
			printf("cmd:%d,  struct:    tem_max:%d tem_min:%d  light_max:%d\
					light_min:%d  hum_max:%d  hum_min:%d\n", cmdbuf.type, old_status.tem_max,old_status.tem_min,\
					old_status.light_max,old_status.light_min,old_status.hum_max,old_status.hum_min);
		}
		else if(cmdbuf.type == GPRS_chg)
		{
			//如果是更改短信号码的命令，则直接修改
			memcpy(&cmdbuf1, &cmdbuf.text[0],sizeof(GPRS_cmd));
			printf("cmd:%d,  修改后的中心密码为为%s  接收短信号码为%s\n", cmdbuf.type, cmdbuf1.center_number, cmdbuf1.to_number);
			set_phno(&pdu, PHNO_RECV, cmdbuf1.to_number);
			set_phno(&pdu, PHNO_CENT, cmdbuf1.center_number);
		}
	}
}



